﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Pavilion;

namespace Pavilion.GameTools
{
    public class AnimationTool : GameToolBase
    {
        public event Action<AnimationNode> AnimationNodeLoaded, AnimationNodeUnloaded;

        public Animation this[string animationName] { get { try { return (ResourceManager.Instance[ResourceName] as AnimationResource)[animationName]; } catch { return null; } } }
        public string ResourceName { get; private set; }
        public LockCreator LockCreator { get; private set; }
        public IEnumerable AnimationList { get { return (ResourceManager.Instance[ResourceName] as AnimationResource).AnimationList; } }
        public ContainerNode RootContainer { get { return (ResourceManager.Instance[ResourceName] as AnimationResource).RootContainer; } }

        private ContainerNodeMonitor containerNodeMonitor;
        private double drawOrder;
        private AnimationPlayer animationPlayer;
        private PhysicalTypeTool physicalTool;

        public override void Initialize(GameItem gameItem)
        {
            LockCreator = new LockCreator(gameItem);

            gameItem.Loaded += (GameItem item) =>
            {
                physicalTool = item.ObtainTool<PhysicalTypeTool>();
                animationPlayer = new AnimationPlayer();
                animationPlayer.ResourceName = ResourceName;

                DrawItem drawItem = new DrawItem(drawOrder, () =>
                {
                    SpriteEffects spriteEffects;

                    if (physicalTool.PolygonData.IsFlippedHorizontally)
                        spriteEffects = SpriteEffects.FlipHorizontally;
                    else
                        spriteEffects = SpriteEffects.None;

                    animationPlayer.Draw(physicalTool.PolygonData.Position, physicalTool.PolygonData.Size, spriteEffects);
                });
                DrawManager.Instance.Add(drawItem);
                item.DrawVisibilityGroup.Add(drawItem);

                LoadNodeMonitor(item);
            };

            gameItem.UpdateLogic += (gameTime) => animationPlayer.Animate(gameTime, physicalTool.PolygonData.Size);

            gameItem.Disposed += () => 
            {
                CurrentAnimation.UnloadAllFrames();
                animationPlayer.Dispose();
                animationPlayer = null;
            };
        }

        public override void Deserialize(XElement xmlToolProperties)
        {
            ResourceName = xmlToolProperties.Attribute("ResourceName").Value;
            drawOrder = double.Parse(xmlToolProperties.Attribute("DrawOrder").Value);
        }

        public override void Serialize() { }

        private void LoadNodeMonitor(GameItem gameItem)
        {
            containerNodeMonitor = new ContainerNodeMonitor(RootContainer, gameItem, Logger.CreateLogger(gameItem.Name));
            containerNodeMonitor.BubbleUpNodeLoaded += new Action<NodeBase>(RootContainer_NodeLoaded);
            containerNodeMonitor.BubbleUpNodeUnloaded += new Action<NodeBase>(RootContainer_BubbleUpNodeUnloaded);
            containerNodeMonitor.Start();
        }

        private void UpdateCurrentAnimation()
        {
            CurrentAnimation = (containerNodeMonitor.ExtractNodeByType(typeof(AnimationNode)) as AnimationNode).Animation;
        }

        public Animation CurrentAnimation
        {
            get
            {
                return animationPlayer.Animation;
            }
            set
            {
                animationPlayer.Animation = value;
            }
        }

        public event Action<Animation> Complete
        {
            add { animationPlayer.Complete += value; }
            remove { animationPlayer.Complete -= value; }
        }

        private void OnAnimationNodeUnloaded(AnimationNode node)
        {
            if (AnimationNodeUnloaded != null)
                AnimationNodeUnloaded(node);
        }

        private void OnAnimationNodeChanged(AnimationNode node)
        {
            if (AnimationNodeLoaded != null)
                AnimationNodeLoaded(node);
        }

        private void RootContainer_BubbleUpNodeUnloaded(NodeBase node)
        {
            if (node is AnimationNode)
            {
                CurrentAnimation = null;
                OnAnimationNodeUnloaded((AnimationNode)node);
            }
        }

        private void RootContainer_NodeLoaded(NodeBase node)
        {
            if (node is AnimationNode)
            {
                CurrentAnimation = (node as AnimationNode).Animation;
                OnAnimationNodeChanged(node as AnimationNode);
            }
        }
    }
}
